package system

import (
	"github.com/spf13/cast"
	"gorm.io/gorm"
	"seven-admin/app/admin/schemas/req"
	"seven-admin/app/admin/schemas/resp"
	"seven-admin/app/collection/system"
	"seven-admin/core/config"
	"seven-admin/core/response"
	"seven-admin/util"
)

type AuthMenuService interface {
	SelectMenuByRoleId(roleId uint, auth *req.AuthReq) (mapList []interface{}, e error)
	List(auth *req.AuthReq) (res []interface{}, e error)
	Detail(id uint, auth *req.AuthReq) (res resp.SystemAuthMenuResp, e error)
	Add(addReq req.SystemAuthMenuAddReq, auth *req.AuthReq) (e error)
	Edit(editReq req.SystemAuthMenuEditReq, auth *req.AuthReq) (e error)
	Del(id uint, auth *req.AuthReq) (e error)
}

type authMenuService struct {
	db      *gorm.DB
	permSrv AuthPermService
}

func (a authMenuService) SelectMenuByRoleId(roleId uint, auth *req.AuthReq) (mapList []interface{}, e error) {
	//TODO implement me
	if auth.IsSuperTenant {
		var menuIds []uint
		if menuIds, e = a.permSrv.SelectMenuIdsByRoleId(cast.ToUint(roleId), auth); e != nil {
			return
		}
		if len(menuIds) == 0 {
			menuIds = []uint{}
		}
		chain := a.db.Where("menu_type in ? AND is_disable = ?", []string{"M", "C"}, 0)
		if !auth.IsSuperAdmin {
			chain = chain.Where("id in ?", menuIds)
		}
		var menus []system.AuthMenu
		err := chain.Order("menu_sort asc, id desc").Find(&menus).Error
		if e = response.CheckErr(err, "SelectMenuByRoleId Find err"); e != nil {
			return
		}
		var menuResps []resp.SystemAuthMenuResp
		response.Copy(&menuResps, menus)
		mapList = util.ArrayUtil.ListToTree(
			util.ConvertUtil.StructsToMaps(menuResps), "id", "pid", "children")
		return
	} else {
		var tenantIds []uint
		a.db.Model(&system.AuthTenantPerm{}).Where("tenant_id = ?", auth.TenantId).Pluck("menu_id", &tenantIds)
		if len(tenantIds) == 0 {
			tenantIds = []uint{}
		}
		chain := a.db.Where("menu_type in ? AND is_disable = ?", []string{"M", "C"}, 0)
		if !auth.SoftSuperAdmin {
			var roleIds []uint
			if roleIds, e = a.permSrv.SelectMenuIdsByRoleId(auth.RoleId, auth); e != nil {
				return
			}
			chain = chain.Where("id in ?", roleIds)
		} else {
			chain = chain.Where("id in ?", tenantIds)
		}
		var menus []system.AuthMenu
		err := chain.Order("menu_sort asc, id desc").Find(&menus).Error
		if e = response.CheckErr(err, "SelectMenuByRoleId Find err"); e != nil {
			return
		}
		var menuResps []resp.SystemAuthMenuResp
		response.Copy(&menuResps, menus)
		mapList = util.ArrayUtil.ListToTree(
			util.ConvertUtil.StructsToMaps(menuResps), "id", "pid", "children")
		return
	}
}

func (a authMenuService) List(auth *req.AuthReq) (res []interface{}, e error) {
	//TODO implement me
	var menus []system.AuthMenu
	if auth.IsSuperTenant {
		err := a.db.Order("menu_sort asc, id desc").Find(&menus).Error
		if e = response.CheckErr(err, "List Find err"); e != nil {
			return
		}
	} else {
		var menuIds []uint
		a.db.Model(&system.AuthTenantPerm{}).Where("tenant_id = ?", auth.TenantId).Pluck("menu_id", &menuIds)
		if len(menuIds) == 0 {
			menuIds = []uint{}
		}
		err := a.db.Where("id in ?", menuIds).Order("menu_sort asc, id desc").Find(&menus).Error
		if e = response.CheckErr(err, "List Find err"); e != nil {
			return
		}
	}

	var menuResps []resp.SystemAuthMenuResp
	response.Copy(&menuResps, menus)
	return util.ArrayUtil.ListToTree(
		util.ConvertUtil.StructsToMaps(menuResps), "id", "pid", "children"), nil
}

func (a authMenuService) Detail(id uint, auth *req.AuthReq) (res resp.SystemAuthMenuResp, e error) {
	//TODO implement me
	var menu system.AuthMenu
	err := a.db.Where("id = ?", id).Limit(1).First(&menu).Error
	if e = response.CheckErrDBNotRecord(err, "菜单已不存在!"); e != nil {
		return
	}
	if e = response.CheckErr(err, "Detail First err"); e != nil {
		return
	}
	response.Copy(&res, menu)
	if menu.MenuType == "C" {
		var actions []system.AuthMenu
		err = a.db.Where("menu_type = ? AND pid = ?", "A", menu.ID).Order("menu_sort asc, id desc").Find(&actions).Error
		if e = response.CheckErr(err, "Detail Find actions err"); e != nil {
			return
		}
		var actionResps []resp.SystemAuthMenuFastActionsResp
		for _, action := range actions {
			actionResps = append(actionResps, resp.SystemAuthMenuFastActionsResp{
				ID:    action.ID,
				Name:  action.MenuName,
				Perms: action.Perms,
				Sort:  action.MenuSort,
			})
		}
		res.Actions = actionResps
	}
	return
}

func (a authMenuService) Add(addReq req.SystemAuthMenuAddReq, auth *req.AuthReq) (e error) {
	//TODO implement me
	var menu system.AuthMenu
	response.Copy(&menu, addReq)
	return a.db.Transaction(func(tx *gorm.DB) error {
		err := tx.Create(&menu).Error
		if e = response.CheckErr(err, "Add Create err"); e != nil {
			return tx.Rollback().Error
		}
		if menu.MenuType == "C" {
			if len(addReq.Actions) > 0 {
				for _, action := range addReq.Actions {
					actionModel := &system.AuthMenu{
						MenuType: "A",
						Pid:      menu.ID,
						MenuName: action.Name,
						Perms:    action.Perms,
						MenuSort: action.Sort,
					}
					err := tx.Create(actionModel).Error
					if e = response.CheckErr(err, "Add Create action err"); e != nil {
						return tx.Rollback().Error
					}
				}
			}
		}
		util.RedisUtil.Del(config.AdminConfig.BackstageRolesKey)
		return nil
	})
}

func (a authMenuService) Edit(editReq req.SystemAuthMenuEditReq, auth *req.AuthReq) (e error) {
	//TODO implement me
	var menu system.AuthMenu
	err := a.db.Where("id = ?", editReq.ID).Limit(1).Find(&menu).Error
	if e = response.CheckErrDBNotRecord(err, "菜单已不存在!"); e != nil {
		return
	}
	if e = response.CheckErr(err, "Edit Find err"); e != nil {
		return
	}
	response.Copy(&menu, editReq)
	return a.db.Transaction(func(tx *gorm.DB) error {
		err := tx.Save(&menu).Error
		if e = response.CheckErr(err, "Edit Save err"); e != nil {
			return tx.Rollback().Error
		}
		if menu.MenuType == "C" {
			if len(editReq.Actions) > 0 {
				var actionIds []uint
				err = tx.Model(&system.AuthMenu{}).Where("menu_type = ? AND pid = ?", "A", menu.ID).Pluck("id", &actionIds).Error
				if e = response.CheckErr(err, "Edit Pluck action ids err"); e != nil {
					return tx.Rollback().Error
				}
				for _, action := range editReq.Actions {
					var actionModel system.AuthMenu
					tx.Where("id = ?", action.ID).First(&actionModel)
					if actionModel.ID == 0 {
						actionModel.Pid = menu.ID
						actionModel.MenuType = "A"
						actionModel.MenuSort = action.Sort
						actionModel.Perms = action.Perms
						actionModel.MenuName = action.Name
						err := tx.Create(&actionModel).Error
						if e = response.CheckErr(err, "Edit Create action err"); e != nil {
							return tx.Rollback().Error
						}
					}
					actionModel.Perms = action.Perms
					actionModel.MenuSort = action.Sort
					actionModel.MenuName = action.Name
					err = tx.Model(&actionModel).Updates(&actionModel).Error
					if e = response.CheckErr(err, "Edit Save action err"); e != nil {
						return tx.Rollback().Error
					}
				}
			}
		}
		util.RedisUtil.Del(config.AdminConfig.BackstageRolesKey)
		return nil
	})
}

func (a authMenuService) Del(id uint, auth *req.AuthReq) (e error) {
	//TODO implement me
	var menu system.AuthMenu
	err := a.db.Where("id = ?", id).Limit(1).First(&menu).Error
	if e = response.CheckErrDBNotRecord(err, "菜单已不存在!"); e != nil {
		return
	}
	if e = response.CheckErr(err, "Delete First err"); e != nil {
		return
	}
	r := a.db.Where("pid = ?", id).Limit(1).Find(&system.AuthMenu{})
	err = r.Error
	if e = response.CheckErr(err, "Delete Find by pid err"); e != nil {
		return
	}
	if r.RowsAffected > 0 {
		return response.AssertArgumentError.Make("请先删除子菜单再操作！")
	}
	err = a.db.Delete(&menu).Error
	e = response.CheckErr(err, "Delete Delete err")
	return
}

func NewAuthMenuService(db *gorm.DB, permSrv AuthPermService) AuthMenuService {
	return &authMenuService{
		db:      db,
		permSrv: permSrv,
	}
}
